`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2017/11/02 15:12:22
// Design Name: 
// Module Name: datapath
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
import structs::*;
`include "cp0.vh"
module datapath(
	input wire clk,rst,
	output datapath_sign_out_t datapathout,
	input controller_sign_out_t controllerout,
	
	input wire[31:0] instrF,
	

	input wire[31:0] readdataM,
	input wire stallreg_from_if,stallreg_from_mem,
	input wire[5:0] ext_int,
	output wire[3:0] datamemEna
    );
	//assign datamemEna = (controllerout.memwriteM==1)?4'b1111:4'b0000;
	
	//fetch stage
	wire stallF;
	wire isindelayslotF;
	wire [31:0]currentinstaddrF;
	//excep
	wire[8:0] exceptF;
	wire[31:0] wrongPCF;
	//FD
	wire[8:0] exceptFD;
	wire [31:0] pcnextFD,pcnextbrFD,pcplus4F,pcbranchD,pcjumpregD,pcnext;
	//decode stage
	wire [31:0] pcplus4D,pcplus4E;
	wire forwardaD,forwardbD;
	wire [4:0] rsD,rtD,rdD,saD;
	wire flushD,stallD; 
	wire [31:0] signimmD,signimmshD;
	wire [31:0] srcaD,srca2D,srcbD,srcb2D;
	wire isindelayslotD;
	wire [31:0] currentinstaddrD;
	//excep
	wire[8:0] exceptD;
	wire[31:0] wrongPCD;
	wire[31:0] breaksyscodeD;
	//wire[31:0] syscallcodeD;
	//DE
	wire[8:0] exceptDE;
	//execute stage
	wire [1:0] forwardaE,forwardbE;
	wire forwardcp0E;
	wire [4:0] rsE,rtE,rdE,saE,rxE;
	wire [4:0] writeregE;
	wire [31:0] signimmE;
	wire [31:0] srcaE,srca2E,srca3E,srca4E,srcbE,srcb2E,srcb3E,srcb4E;
	wire [31:0] aluoutE;
	wire alubusyE;
	wire [31:0] cp0readdataE,cp0readdata2E;
	wire isindelayslotE;
	wire [31:0]currentinstaddrE;
	//excep
	wire[8:0] exceptE;
	wire[31:0] wrongPCE;
	wire[31:0] breaksyscodeE;
	//wire[31:0] syscallcodeE;
	wire aluoverflow;
	//mem stage
	wire [4:0] writeregM;
    wire isindelayslotM;
    wire [31:0]currentinstaddrM;	//EM
	wire[8:0] exceptEM;
	wire[31:0] bad_addr_M;
	//excep
	wire[8:0] exceptM;
	wire[31:0] wrongPCM;
	wire[31:0] breaksyscodeM;
	wire addrload;
	wire addrstore;
	//wire[31:0] syscallcodeM;
	// load moudle
	wire [31:0] newReaddataM;
	// store moudle
	wire [31:0] oldwritedataM;
	// cp0 write /read addr
	wire [4:0] cp0waddrM;
	// cp0 data read out
	wire [31:0] cp0readdataM;
	wire [31:0] cp0_status,cp0_cause,cp0_epc;
	wire [31:0] excepttypeM;
	wire [31:0] except_jump_targetM;
	
	//writeback stage
	wire [4:0] writeregW;
	wire [31:0] aluoutW,readdataW,wbackdataW,resultW;
	assign datapathout.resultW = resultW;
	wire [31:0] cp0readdataW;
	assign datapathout.writeregW = writeregW;
	// debug
	wire [31:0] debugPCF;
	wire [31:0] debugPCD;
	wire [31:0] debugPCE;
	wire [31:0] debugPCM;
	wire [31:0] debugPCW;
	assign datapathout.debugPCW = debugPCW;
	//hazard detection
	hazard h(
		//fetch stage
		stallF,

		//decode stage
		rsD,rtD,
		controllerout.branchD,
		controllerout.regdstD,
		controllerout.jumpregD,
		forwardaD,forwardbD,
		flushD,
		stallD,

		//execute stage
		rsE,rtE,
		writeregE,
		controllerout.regwriteE,
		controllerout.memtoregE,
		alubusyE,
		controllerout.cp0readE,
		rdE,
		forwardaE,forwardbE,
		datapathout.flushE,
		datapathout.stallE,
		forwardcp0E,

		//mem stage
		writeregM,
		controllerout.regwriteM,
		controllerout.memtoregM,
		controllerout.cp0weM,
		cp0waddrM,
		datapathout.except_jumpM,
		datapathout.flushM,
		datapathout.stallM,

		//write back stage
		writeregW,
		controllerout.regwriteW,
		datapathout.flushW,
		datapathout.stallW,

		//AXI
		stallreg_from_if,
		stallreg_from_mem
		);

	//next PC logic (operates in fetch an decode)
	mux2 #(32) pcbrmux(pcplus4F,pcbranchD,controllerout.pcsrcD,pcnextbrFD);
	mux2 #(32) pcmux(pcnextbrFD,
		{pcplus4D[31:28],datapathout.instrD[25:0],2'b00},
		controllerout.jumpD,pcnextFD);
    // next pc, compare pcnextFD with jumpreg pc
    mux2 #(32) pcjmpmux(pcnextFD, srca2D, controllerout.jumpregD, pcjumpregD);
	mux2 #(32) pcexceptmux(pcjumpregD, except_jump_targetM, datapathout.except_jumpM, pcnext);
    
	//regfile (operates in decode and writeback)
	regfile rf(clk,controllerout.regwriteW & ~datapathout.stallW,rsD,rtD,writeregW,resultW,srcaD,srcbD);

	//fetch stage logic
	//exceptionF
	assign exceptF = datapathout.pcF[1:0]==2'b00 ? 9'b000000000 : 9'b010000000;
	assign wrongPCF = datapathout.pcF;
	//
	pc #(32) pcreg(clk,rst,~stallF,pcnext,datapathout.pcF);
	assign debugPCF = datapathout.pcF;
	adder pcadd1(datapathout.pcF,32'b100,pcplus4F);
	// detect whether the inst of f stage is in the delay slot
	assign isindelayslotF = (controllerout.branchD || controllerout.jumpD || controllerout.jumpregD);
	assign currentinstaddrF = datapathout.pcF;
	assign datapathout.stallF = stallF;
	
	//decode stage
	flopenrc #(32) debugD(clk,rst,~stallD,flushD,debugPCF,debugPCD);
	flopenrc #(32) r1D(clk,rst,~stallD,flushD,pcplus4F,pcplus4D);
	flopenrc #(32) r2D(clk,rst,~stallD,flushD,instrF,datapathout.instrD);
	flopenrc #(1) r3D(clk,rst,~stallD,flushD,isindelayslotF,isindelayslotD);
	flopenrc #(32) r4D(clk,rst,~stallD,flushD,currentinstaddrF,currentinstaddrD);
	signext se(datapathout.instrD[31:26],datapathout.instrD[15:0],signimmD);
	sl2 immsh(signimmD,signimmshD);
	adder pcadd2(pcplus4D,signimmshD,pcbranchD);
	mux2 #(32) forwardamux(srcaD,datapathout.aluoutM,forwardaD,srca2D);
	mux2 #(32) forwardbmux(srcbD,datapathout.aluoutM,forwardbD,srcb2D);
	//exceptionRegFD
	flopenrc #(9) exceptRD(clk,rst,~stallD,flushD,exceptF,exceptFD);
	flopenrc #(32) wrongPCRD(clk,rst,~stallD,flushD,wrongPCF,wrongPCD);
	
	// compare reg and reg or reg and 0; equalD means differently, not a == b;
	cmp comp(srca2D,srcb2D,controllerout.cmpcontrolD,datapathout.equalD);
	//eqcmp comp(srca2D,srcb2D,datapathout.equalD);
	assign datapathout.opD = datapathout.instrD[31:26];
	assign datapathout.functD = datapathout.instrD[5:0];
	assign rsD = datapathout.instrD[25:21];
	assign rtD = datapathout.instrD[20:16];
	assign rdD = datapathout.instrD[15:11];
	assign saD = datapathout.instrD[10:6];
    //exceptionD
	assign exceptD = {exceptFD[8:7],controllerout.invalidD,exceptFD[5],controllerout.breakpointD,controllerout.syscallD,exceptFD[2:1],controllerout.eretD};
	assign breaksyscodeD = datapathout.instrD[25:6];
	flopenrc #(9) exceptRE(clk,rst,~datapathout.stallE,datapathout.flushE,exceptD,exceptDE);
	flopenrc #(32) wrongPCRE(clk,rst,~datapathout.stallE,datapathout.flushE,wrongPCD,wrongPCE);
	flopenrc #(20) breaksyscodeRE(clk,rst,~datapathout.stallE,datapathout.flushE,breaksyscodeD,breaksyscodeE);
   // select the elements to be sent to srcae and srcbe
   // mux #(32) srcadpcp4mux(srcaD,pcplus4D,controllerout.jumplinkD,srca3D);
   // mux #(32) srcbd4mux(srcbD,32'b0100,controllerout.jumplinkD,srcb3D);
    
	//execute stage
	flopenrc #(32) debugE(clk,rst,~datapathout.stallE,datapathout.flushE,debugPCD,debugPCE);
	flopenrc #(32) r1E(clk,rst,~datapathout.stallE,datapathout.flushE,srcaD,srcaE);
	flopenrc #(32) r2E(clk,rst,~datapathout.stallE,datapathout.flushE,srcbD,srcbE);
	flopenrc #(32) r3E(clk,rst,~datapathout.stallE,datapathout.flushE,signimmD,signimmE);
	flopenrc #(5) r4E(clk,rst,~datapathout.stallE,datapathout.flushE,rsD,rsE);
	flopenrc #(5) r5E(clk,rst,~datapathout.stallE,datapathout.flushE,rtD,rtE);
	flopenrc #(5) r6E(clk,rst,~datapathout.stallE,datapathout.flushE,rdD,rdE);
	flopenrc #(32) r8E(clk,rst,~datapathout.stallE,datapathout.flushE, pcplus4D,pcplus4E);
    flopenrc #(5) r7E(clk,rst,~datapathout.stallE,datapathout.flushE,saD,saE);
    flopenrc #(1) r9E(clk,rst,~datapathout.stallE,datapathout.flushE,isindelayslotD,isindelayslotE);
	flopenrc #(32) r10E(clk,rst,~datapathout.stallE,datapathout.flushE,currentinstaddrD,currentinstaddrE);
	mux3 #(32) forwardaemux(srcaE,resultW,datapathout.aluoutM,forwardaE,srca2E);
	mux2 #(32) srca2epcp4mux(srca2E,pcplus4E,controllerout.jumplinkE,srca3E);
	mux2 #(32) srca3ecp0readdata2emux(srca3E,cp0readdata2E,controllerout.cp0readE,srca4E);

	mux3 #(32) forwardbemux(srcbE,resultW,datapathout.aluoutM,forwardbE,srcb2E);
	mux2 #(32) srcbmux(srcb2E,signimmE,controllerout.alusrcE,srcb3E);
	mux2 #(32) srcb3e4mux(srcb3E,32'b0100,controllerout.jumplinkE,srcb4E);
	alu alu(clk,rst,datapathout.stallE,datapathout.except_jumpM,srca4E,srcb4E,controllerout.alucontrolE,saE,aluoutE,alubusyE,aluoverflow);
	// select writeregE from rtE, rdE, r31E
	mux2 #(5) wrmux(rtE,rdE,controllerout.regdstE,rxE);
	// select rxE or r31E
    mux2 #(5) rxer31emux(rxE,5'b11111,controllerout.jumplinkE^controllerout.jumplinkregE,writeregE);
    
    // cp0
	cp0_reg cp0(
	       .clk(clk),.rst(rst),
		   .raddr_i(rdE),.data_o(cp0readdataE), //E阶段�???
		   .we_i(controllerout.cp0weM & ~datapathout.stallM),.waddr_i(cp0waddrM),.data_i(datapathout.aluoutM), //M阶段�???
	    	.current_inst_addr_i(currentinstaddrM),.is_in_delayslot_i(isindelayslotM),.int_i(ext_int),.excepttype_i(excepttypeM),.bad_addr_i(bad_addr_M),
			.status_o(cp0_status),.cause_o(cp0_cause),.epc_o(cp0_epc)
	);
	
	// use forwardcp0E to select cp0readdataE or aluoutM
	mux2 #(32) forwardcp0mux(cp0readdataE,datapathout.aluoutM,forwardcp0E,cp0readdata2E);
	//exceptionE
	assign exceptE = {exceptDE[8:6],aluoverflow,exceptDE[4:0]};
	flopenrc #(9) exceptRM(clk,rst,~datapathout.stallM,datapathout.flushM,exceptE,exceptEM);
	flopenrc #(32) wrongPCRM(clk,rst,~datapathout.stallM,datapathout.flushM,wrongPCE,wrongPCM);
	flopenrc #(20) breaksyscodeRM(clk,rst,~datapathout.stallM,datapathout.flushM,breaksyscodeE,breaksyscodeM);
	


	//mem stage
    flopenrc #(32) debugM(clk,rst,~datapathout.stallM,datapathout.flushM,debugPCE,debugPCM);
	flopenrc #(32) r1M(clk,rst,~datapathout.stallM,datapathout.flushM,srcb2E,oldwritedataM);
	flopenrc #(32) r2M(clk,rst,~datapathout.stallM,datapathout.flushM,aluoutE,datapathout.aluoutM);
	flopenrc #(5) r3M(clk,rst,~datapathout.stallM,datapathout.flushM,writeregE,writeregM);
	flopenrc #(5) r4M(clk,rst,~datapathout.stallM,datapathout.flushM,rdE,cp0waddrM);
    //flopenrc #(32) r5M(clk,rst,~datapathout.stallM,datapathout.flushM,cp0readdata2E,cp0readdataM);
    flopenrc #(1) r6M(clk,rst,~datapathout.stallM,datapathout.flushM,isindelayslotE,isindelayslotM);
	flopenrc #(32) r7ME(clk,rst,~datapathout.stallM,datapathout.flushM,currentinstaddrE,currentinstaddrM);
	//lord
	lord_t lt(datapathout.aluoutM[1:0],controllerout.lordM,readdataM,newReaddataM,addrload);
	//store
	store_t st(controllerout.memwriteM,datapathout.aluoutM[1:0],controllerout.storeM,oldwritedataM,datapathout.writedataM,datamemEna,addrstore);
	//exception
	exception exception(rst,exceptM,cp0_status,cp0_cause,cp0_epc,excepttypeM,except_jump_targetM,datapathout.except_jumpM);
	assign bad_addr_M = exceptM[`EXC_SIGNAL_ADELI]?wrongPCM:datapathout.aluoutM;

	//exceptionE
	assign exceptM = {exceptEM[8:3],addrload,addrstore,exceptEM[0]};
	
	
	
	//writeback stage
	flopenrc #(32) debugW(clk,rst,~datapathout.stallW,datapathout.flushW,debugPCM,debugPCW);
	flopenrc #(32) r1W(clk,rst,~datapathout.stallW,datapathout.flushW,datapathout.aluoutM,aluoutW);
	flopenrc #(32) r2W(clk,rst,~datapathout.stallW,datapathout.flushW,newReaddataM,readdataW);
	flopenrc #(5) r3W(clk,rst,~datapathout.stallW,datapathout.flushW,writeregM,writeregW);
	//flopenrc #(32) r4W(clk,rst,~datapathout.stallW,datapathout.flushW,cp0readdataM,cp0readdataW);
	mux2 #(32) resmux(aluoutW,readdataW,controllerout.memtoregW,resultW);
	// select cp0readdata or wbackdataW !!!! deprecated,instead by alu reusing
	//mux2 #(32) cp0readdataresultWmux(wbackdataW,cp0readdataW,controllerout.cp0readW,resultW);
	
endmodule
